//
//  NSData+Encryption.m
//  vpntest
//
//  Created by glendale on 13-7-9.
//  Copyright (c) 2013年 glendale. All rights reserved.
//

#import "NSData+Encryption.h"

#import <CommonCrypto/CommonCryptor.h>

static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";

@implementation NSData (Encryption)


//加密
- (NSData *)AES256EncryptWithKey:(NSString *)key {
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
    
    // fetch key data
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    NSUInteger dataLength = [self length];
    
    //See the doc: For block ciphers, the output size will always be less than or
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    
    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding|kCCOptionECBMode,
                                          keyPtr, kCCKeySizeAES128,
                                          NULL /* initialization vector (optional) */,
                                          [self bytes], dataLength, /* input */
                                          buffer, bufferSize, /* output */
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }
    
    free(buffer); //free the buffer;
    return nil;
}


//解密

- (NSData *)AES256DecryptWithKey:(NSString *)key {
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
    
    // fetch key data
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    NSUInteger dataLength = [self length];
    
    //See the doc: For block ciphers, the output size will always be less than or
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    
    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding|kCCOptionECBMode,
                                          keyPtr, kCCKeySizeAES128,
                                          NULL /* initialization vector (optional) */,
                                          [self bytes], dataLength, /* input */
                                          buffer, bufferSize, /* output */
                                          &numBytesDecrypted);
    
    if (cryptStatus == kCCSuccess) {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }
    
    free(buffer); //free the buffer;
    return nil;
}


- (NSString *)newStringInBase64FromData           //追加64编码
{

    NSMutableString *dest = [[NSMutableString alloc] initWithString:@""];
    
    unsigned char * working = (unsigned char *)[self bytes];
    
    int srcLen = [self length];
    
    for (int i=0; i<srcLen; i += 3) {
        
        for (int nib=0; nib<4; nib++) {
            
            int byt = (nib == 0)?0:nib-1;
            
            int ix = (nib+1)*2;
            
            if (i+byt >= srcLen) break;
            
            unsigned char curr = ((working[i+byt] << (8-ix)) & 0x3F);
            
            if (i+nib < srcLen) curr |= ((working[i+nib] >> ix) & 0x3F);
            
            [dest appendFormat:@"%c", base64[curr]];
            
        }
        
    }
    
    return dest;
  
    
}

+ (NSString*) base64Encode:(NSData *)data

{
    
    static char base64EncodingTable[64] = {
        
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
        
        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
        
        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
        
        'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
        
    };
    
    int length = [data length];
    
    unsigned long ixtext, lentext;
    
    long ctremaining;
    
    unsigned char input[3], output[4];
    
    short i, charsonline = 0, ctcopy;
    
    const unsigned char *raw;
    
    NSMutableString *result;
    
    
    
    lentext = [data length];
    
    if (lentext < 1)
        
        return @"";
    
    result = [NSMutableString stringWithCapacity: lentext];
    
    raw = [data bytes];
    
    ixtext = 0;
    
    
    
    while (true) {
        
        ctremaining = lentext - ixtext;
        
        if (ctremaining <= 0)
            
            break;
        
        for (i = 0; i < 3; i++) {
            
            unsigned long ix = ixtext + i;
            
            if (ix < lentext)
                
                input[i] = raw[ix];
            
            else
                
                input[i] = 0;
            
        }
        
        output[0] = (input[0] & 0xFC) >> 2;
        
        output[1] = ((input[0] & 0x03) << 4) | ((input[1] & 0xF0) >> 4);
        
        output[2] = ((input[1] & 0x0F) << 2) | ((input[2] & 0xC0) >> 6);
        
        output[3] = input[2] & 0x3F;
        
        ctcopy = 4;
        
        switch (ctremaining) {
                
            case 1:
                
                ctcopy = 2;
                
                break;
                
            case 2:
                
                ctcopy = 3;
                
                break;
                
        }
        
        
        
        for (i = 0; i < ctcopy; i++)
            
            [result appendString: [NSString stringWithFormat: @"%c", base64EncodingTable[output[i]]]];
        
        
        
        for (i = ctcopy; i < 4; i++)
            //%3d =号
            [result appendString:@"%3D"];
        
        
        
        ixtext += 3;
        
        charsonline += 4;
        
        
        
        if ((length > 0) && (charsonline >= length))
            
            charsonline = 0;
        
    }
    
    /*
     将编码后的特殊字符转换成 转义的16进制
     '+'->%2B
     '/'->%2F
     */
    NSString *strResultTmp = @"";
    for (int i = 0; i<[result length]; i++)
    {
        NSString *strCutTmp = [result substringWithRange:NSMakeRange(i, 1)];
        if ([strCutTmp isEqualToString:@"+"])
        {
            strCutTmp = @"%2B";
        }
        else if ([strCutTmp isEqualToString:@"/"])
        {
            strCutTmp = @"%2F";
        }
        strResultTmp = [strResultTmp stringByAppendingString:strCutTmp];
    }
    result = [NSMutableString stringWithString:strResultTmp];
    
    return result;
    
}

+ (NSString*)base64encode:(NSString*)str

{
    
    if ([str length] == 0)
        
        return @"";
    
    const char *source = [str UTF8String];
    
    int strlength  = strlen(source);
    
    char *characters = malloc(((strlength + 2) / 3) * 4);
    
    if (characters == NULL)
        
        return nil;
    
    NSUInteger length = 0;
    
    NSUInteger i = 0;
    
    while (i < strlength) {
        
        char buffer[3] = {0,0,0};
        
        short bufferLength = 0;
        
        while (bufferLength < 3 && i < strlength)
            
            buffer[bufferLength++] = source[i++];
        
        characters[length++] = base64[(buffer[0] & 0xFC) >> 2];
        
        characters[length++] = base64[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)];
        
        if (bufferLength > 1)
            
            characters[length++] = base64[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)];
        
        else characters[length++] = '=';
        
        if (bufferLength > 2)
            
            characters[length++] = base64[buffer[2] & 0x3F];
        
        else characters[length++] = '=';
        
    }
    
    NSString *g = [[NSString alloc] initWithBytesNoCopy:characters length: length encoding:NSUTF8StringEncoding freeWhenDone:YES] ;
    

    return g;
    
}


@end
